home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Animation Tutorial / AnimationTutorial.c < prev    next >
C/C++ Source or Header  |  1995-05-23  |  15KB  |  335 lines

  1. /****************************************************************************************
  2. Did you ever want to learn about CopyBits() animation? Do you wonder what the heck is 
  3. CopyBits() anyway? Well this file is for you, it gives a little information on the 
  4. theory of CopyBits() animation and then provides some well documented source code that
  5. creates a simple screen saver by animating icons. If you have any questions please E-mail 
  6. me on America On-Line at Bernard256. By the way this requires 32 bit QuickDraw. I've tested this
  7. in 1 to 16 bit color and it works though you have to increase the memory for 16 bit. 
  8. Feel free to upload this anywhere but if you want to change it let me know so
  9. I can make the changes. Please send me criticisms :-) I compiled this on CodeWarrior but 
  10. there is no reason for it not to work on Think C.
  11. *****************************************************************************************
  12. Some info on CopyBits() animation: If you already know the theories behind CopyBits()
  13. animation skip this and get to the code otherwise...
  14. First of all there are two parts to animation first of all displaying the various frames
  15. in a row and second moving the moving frames around the screen. This program animates 
  16. some icons found in the resource fork of this program. 
  17. Here is basically how you animate on the Macintosh. First you create an Offscreen Graphic
  18. World which is basically a place were you can draw things but they don't show up on the
  19. screen. Then you draw all the things you are gonna animate onto this Offscreen world. 
  20. Then you use CopyBits(), a system routine, to Copy the frame you are up to in your 
  21. animation from offscreen to onscreen, then you do the next frame and so on. The obvious
  22. question is why go through all the trouble, why not just draw your icons or whatever 
  23. directly on to the screen. Well there are two reasons. 
  24.         1.Speed: CopyBits() is a lot faster then reading from a resource. So it's faster
  25.           to have all your stuff on an Offscreen G World and just copy them when needed
  26.           then to read them from a resource when needed. You will notice however that
  27.           this program doesn't take advantage of this speed gain because actually what
  28.           it does is write from the resource to the offscreen world and then from the 
  29.           offscreen to the real screen. This might seem like it would actually be slower
  30.           then drawing directly to the screen from the resource and in fact it might be
  31.           but this leads us to the next reason for using CopyBits()
  32.         2. Flicker Free Animation: Did you ever notice how when you draw a picture to the
  33.            screen or just open a picture with a drawing program how it scrolls down the
  34.            screen. Well no matter how fast it does this the fact is it does it. If you 
  35.            tried to do animation with this the picture would look like it was flickering.
  36.            However when CopyBits() copies graphics from one port to another it just puts
  37.            it up on the screen in one move. BAM! it's up there. This is the main 
  38.            advantage to CopyBits().
  39. So here is some code using CopyBits() what it does is read the appropriate icons out
  40. of a bunch of Icon Suites (ICN#,icl4,or icl8) and animates them and then bounces them
  41. around the screen. 
  42. ***************************************************************************************/
  43.  
  44. #include<QDOffscreen.h>  /*you have to include these                */
  45. #include<Icons.h> /*headers to do offscreen and icons suites*/
  46.  
  47. #define BEG 128 /*this defines at which id number your icons begin so if you have 10
  48.                   icons from 800 to 810 set this to 800*/
  49. #define MAX 11 /*this is the number of icons you have*/
  50. #define MOVE_DISTANCE 3 /*this is the distance you move the icons while animating*/
  51.  
  52. void ToolBoxInit(void); /*this just does some basic ToolBox Initilization Stuff*/
  53. int MyRandom(int num);  /*this gets random numbers*/
  54. void Animate(void);        /*this draws the next frame and moves it over a little*/
  55. void InitOtherStuff(void); /*initilizes the offscreen gworld and some other stuff*/
  56.  
  57. WindowPtr myWindow; /*pointer to the main window*/
  58. CGrafPtr origPtr;    /*CGrafPtr to main window*/
  59. GDHandle origGDH;   /*GDHandle to main window*/
  60. GWorldPtr offscreen; /*pointer to offscreen gWorld*/
  61. Rect iconRect;        /*theRect the icon is in*/
  62. PixMapHandle myMap; /*pixel map for the gWorld*/
  63. int i, oldHeight;   /*you'll see what these are for later*/
  64. Boolean NE,NW,SE,SW;/*one of these will always be true and the others false the one thats
  65.                       true tells which direction the icon is going. NorthEast NorthWest etc...*/
  66.  
  67.  
  68.  
  69. void main(void)
  70. {    
  71.     ToolBoxInit();    /*Do intilizations*/
  72.     InitOtherStuff();
  73.     NE = NW = SE = SW = FALSE;
  74.  
  75.     switch(MyRandom(4))  /*get a random number between one and four to tell us which 
  76.                             direction we are heading when we start*/
  77.     {
  78.         case 0:
  79.             NE = TRUE;
  80.             break;
  81.         case 1:
  82.             SE = TRUE;
  83.             break;
  84.         case 2:
  85.             SW = TRUE;
  86.             break;
  87.         case 3:
  88.             NW = TRUE;
  89.             break;
  90.     }
  91.     i = 0; 
  92.     while(!Button()) /*while the user hasen't clicked*/
  93.         Animate();   /*advance one frame*/
  94.     LMSetMBarHeight(oldHeight); /*set the menu back to original height (see InitOtherStuff()
  95.                                 to see how we messed around with the menuBar)*/
  96. }
  97.  
  98. void ToolBoxInit(void)
  99. {
  100.     InitGraf(&qd.thePort);
  101.     InitFonts();
  102.     FlushEvents(everyEvent,0);
  103.     InitWindows();
  104.     InitMenus();
  105.     TEInit();
  106.     InitDialogs(0L);
  107.     InitCursor();
  108.     MaxApplZone();
  109. }
  110. void InitOtherStuff(void)
  111. {
  112.     int h,v;
  113.     
  114.     GetDateTime((unsigned long*)&qd.randSeed);/*get a new seed every time*/
  115.     myWindow = NewWindow(nil,&(qd.screenBits.bounds), "\p",TRUE,plainDBox,(WindowPtr)-1L,
  116.                             FALSE,0); /*create a new window as big as the screen*/
  117.     ShowWindow(myWindow); /*show that window*/
  118.     oldHeight = LMGetMBarHeight(); /*save the menu bar height*/
  119.     LMSetMBarHeight(0); /*the set it to zero. You should not here that on 68k Macs 
  120.                             LMGetMBarHeight() is not a function (I think) but a wierd
  121.                             kind of variable that holds the MBar's height*/
  122.     RectRgn(myWindow->visRgn,&myWindow->portRect);/*We are gonna set the item's visRgn
  123.                                                     to the window's portRect so that
  124.                                                     it obscures the menubar.*/
  125.     GetGWorld(&origPtr,&origGDH); /*save the parameters of myWindow so we can get
  126.                                     back to it*/
  127.     NewGWorld(&offscreen,0,&myWindow->portRect,nil,nil,0); /*make a new offscreen g world
  128.                                                              as big as myWindow*/
  129.     myMap = GetGWorldPixMap(offscreen);  /*get the pixel map for that gWorld*/
  130.     LockPixels(myMap);                    /*lock it. you have to do this otherwise it might
  131.                                           get messed up by other stuff going on*/
  132.     SetGWorld(offscreen,0L);            /*now make this gWorld the curent port*/
  133.     h = MyRandom(qd.screenBits.bounds.right-32); /*get a random nubmer that is at least 32
  134.                                                 pixels away from the right of the screen
  135.                                                 this is goin to be our horizantal cord.
  136.                                                 for our icon*/
  137.     v = MyRandom(qd.screenBits.bounds.bottom-32);/*get the vertical coordinate*/
  138.     SetRect(&iconRect,h,v,h+32,v+32);         /*now set the rect with the h and v coord.
  139.                                                 as well as two other points 32 pixels 
  140.                                                 away so its the size of an icon.*/
  141.     EraseRect(&offscreen->portRect);        /*erase the offscreen graphics port if you 
  142.                                               don't do this you will have beautiful 
  143.                                               multi-colored garbage in your port*/
  144.     HideCursor();                            /*hide the mouse cursor*/
  145.     UnlockPixels(myMap);                    /*unlock the pixels otherwise weird stuff
  146.                                               will happen*/
  147. }
  148.  
  149. int MyRandom(int num)
  150. {
  151.     int l;
  152.     
  153.     l=Random(); /*make l a random number between -3 thousand something and 3 thousand 
  154.                   something*/
  155.     if (l==0) /*if l is 0 just return it right away. I don't like doing math with 0*/
  156.         return l;
  157.     if(l<0) /*if l is negative make it positive by multiplying by -1*/
  158.         l*=-1;
  159.     l=l%num; /*what this does is divide l by the num parameter and tell you the remainder
  160.                so if l is 10 and num is 4 then you will get 2 (10/4 is 2 remainder 2). 
  161.                What this does is only return 'num' possible numbers so if num is 4 you
  162.                can only return between 0 and 3*/
  163.     return l;  /*return it!*/
  164. }
  165.  
  166. void Animate(void) /*now for the fun*/
  167. {
  168.         myMap = GetGWorldPixMap(offscreen); /*get the pixMap again*/
  169.         LockPixels(myMap);   /*and lock it*/
  170.         SetGWorld(offscreen,0L); /*set the offscreen gworld to the current port*/
  171.         
  172.         if(NE) /*if we should be going northeast*/
  173.         {
  174.             if(iconRect.right < qd.screenBits.bounds.right && 
  175.                                 iconRect.top > qd.screenBits.bounds.top) /*and we aren't 
  176.                                                                         moving of the
  177.                                                                         right or top
  178.                                                                         of the screen*/
  179.                 OffsetRect(&iconRect,MOVE_DISTANCE,-MOVE_DISTANCE); /*move the rect a
  180.                                                                       a little NE*/
  181.             else /*if we are going of the top or right*/
  182.             {
  183.                 if(iconRect.right >= qd.screenBits.bounds.right)/*were going of the right*/
  184.                 {
  185.                     NE=FALSE;   /*bounce us back so now were going NW*/
  186.                     NW=TRUE;
  187.                 }
  188.                 if(iconRect.top <= qd.screenBits.bounds.top) /*if we are hitting the bottom
  189.                                                             bounce us up to the NE*/
  190.                 {
  191.                     NE=FALSE;
  192.                     SE=TRUE;
  193.                 }
  194.             }/* all the rest of these algorithims just bounce us in different directions
  195.                 so I won't go into them*/
  196.         }
  197.         else if(NW)
  198.         {
  199.             if(iconRect.left > qd.screenBits.bounds.left && 
  200.                                 iconRect.top > qd.screenBits.bounds.top)
  201.                 OffsetRect(&iconRect,-MOVE_DISTANCE,-MOVE_DISTANCE);
  202.             else
  203.             {
  204.                 if(iconRect.left <= qd.screenBits.bounds.left)
  205.                 {
  206.                     NW=FALSE;
  207.                     NE=TRUE;
  208.                 }
  209.                 if(iconRect.top <= qd.screenBits.bounds.top)
  210.                 {
  211.                     NW=FALSE;
  212.                     SW=TRUE;
  213.                 }
  214.             }
  215.         }
  216.         else if(SE)
  217.         {
  218.             if(iconRect.right < qd.screenBits.bounds.right && 
  219.                                 iconRect.bottom < qd.screenBits.bounds.bottom)
  220.                 OffsetRect(&iconRect,MOVE_DISTANCE,MOVE_DISTANCE);
  221.             else
  222.             {
  223.                 if(iconRect.right >= qd.screenBits.bounds.right)
  224.                 {
  225.                     SE=FALSE;
  226.                     SW=TRUE;
  227.                 }
  228.                 if(iconRect.bottom >= qd.screenBits.bounds.bottom)
  229.                 {
  230.                     SE=FALSE;
  231.                     NE=TRUE;
  232.                 }
  233.             }
  234.         }
  235.         else if(SW)
  236.         {
  237.             if(iconRect.left > qd.screenBits.bounds.left && 
  238.                         iconRect.bottom < qd.screenBits.bounds.bottom)
  239.                 OffsetRect(&iconRect,-MOVE_DISTANCE,MOVE_DISTANCE);
  240.             else
  241.             {
  242.                 if(iconRect.left <= qd.screenBits.bounds.left)
  243.                 {
  244.                     SW=FALSE;
  245.                     SE=TRUE;
  246.                 }
  247.                 if(iconRect.bottom >= qd.screenBits.bounds.bottom)
  248.                 {
  249.                     SW=FALSE;
  250.                     NW=TRUE;
  251.                 }
  252.             }
  253.         } /*Now we have the rect in the right place*/
  254.         
  255.         FillRect(&offscreen->portRect,(ConstPatternParam)&qd.black);/*make the g world black cause it looks
  256.                                                cool and will erase the last drawing
  257.                                                we did*/
  258.         PlotIconID(&iconRect,atNone,ttNone,BEG+i);/*plot you icon in iconRect. We are
  259.                                                     plotting the icon that is Id BEG plus
  260.                                                     which ever one we are up to, which
  261.                                                     is determined by i, i is incremented
  262.                                                     at the end of the routine*/
  263.         SetGWorld(origPtr,origGDH); /*this is important before you call CopyBits() make
  264.                                       sure to make the window you are copying TO the
  265.                                       current port. If you don't the routine is 6 times
  266.                                       slower*/
  267.         SetPort(myWindow);
  268.         CopyBits( (BitMap *)(&offscreen->portPixMap),&myWindow->portBits,&offscreen->portRect,
  269.                         &myWindow->portRect, srcCopy,nil);/*this is the crux of it all
  270.                                                             we are copying from the 
  271.                                                             offscreen pixMap to myWindow's
  272.                                                             pixmap. And copying the whole
  273.                                                             offscreen rect to the main
  274.                                                             window's rect. We are using
  275.                                                             the srcCopy mode which just
  276.                                                             copys over anything already
  277.                                                             there*/
  278.         UnlockPixels(myMap); /*now unlock the gWorld's pixels*/
  279.         
  280.         i = (i < MAX-1) ? i+1 : 0; 
  281.                     /*now we have to increment i so next time we'll draw the next frame
  282.                      of the animation. if i is less then the MAX-1 (it has to be 1 less
  283.                     than MAX because we call this after we do the routine so it won't
  284.                     catch i until after it's been used) we have just make it
  285.                     the next one. if i is Max then set it back to the begining*/
  286.         
  287. }
  288.  
  289. /****************************************************************************************
  290. That's it!! This is the basics of CopyBits() animation. Of course I just learned this
  291. a little while ago so I make no guarantees. There probably is a lot of bad code. Sorry ;-).
  292. For more info on all the graphic stuff like offscreen GWorlds see Inside Macintosh: 
  293. Imaging with QuickDraw. For more on icon suites see Inside Macintosh: More Macintosh
  294. ToolBox.
  295.  
  296. What can you do with this or what should I have done to make it faster?
  297.  
  298.     1. Well to make it faster there are a couple things I could have done:
  299.         a)If a I copied all the icons to the offscreen world and then just put them on
  300.           the window when I needed them it would have been faster then redrawing them on
  301.           the gWorld every time I called Animate().
  302.         b)If I hadn't copied the whole portRect every time just the part that needed to
  303.           be updated then it would have been faster also.
  304.     2. If you make it move the frame a further distance it will appear to be faster. This
  305.        is cheating though. Sort of like making a movie faster by taking out every other
  306.        frame. The quality will seriously deteriorate if you do this.
  307.     3. There are other faster ways of doing animation. You can use CopyMask() which is
  308.         kind of like CopyBits() but it uses a Mask to know what parts to copy. It is 
  309.         faster but I'm not sure how to do it.
  310.     4. Set the colors to 1 bit or 4 bit instead of 8 bit. These makes it faster. In
  311.         fact on my computer (6100/60) the black and white is too fast.
  312.     5. I could have used something called Pixel Blitting which gives you the fastest
  313.         animation, I hear. But I haven't the foggiest idea what it is. For more info on
  314.         it and all fast animation routines download the file SpriteWorld. I'm in the 
  315.         midst of going through it and it's great.
  316.     6. How else could it be better? Better graphics. The graphics I used are horrible;
  317.         sorry I'm not an artist. Nevertheless the fact is one of the keys to animation is 
  318.         smooth frames. Take the game Maelstrom for instance. It wouldn't be the same
  319.         if not for the great graphics. Try using icons from your favorite game with
  320.         this program.
  321.     7. You can fool around with this. Try doing more the one icon by making Animate() a
  322.         more general routing that takes maybe a rect and a direction as parameters.
  323.         Try doing it using CIcons or Picts. 
  324.     8. This should probably have error checking, including checking to see if the
  325.        the system has 32 bit QuickDraw, but I was to lazy to put it in. On of the main
  326.        errors you get with CopyBits() is running out of memory.
  327.        
  328.        
  329.         
  330. Whatever you do have fun and be sure to check out the Macintosh Development Forum
  331. on America On-Line. E-Mail me with questions AOL: Bernard256
  332.                                              Internet: Bernard256@aol.com
  333.  
  334. ****************************************************************************************/
  335.